- 本站的镜像网站: GitHub Pages, 个人网站
- 本人的 CSDN 博客: 点这里起飞
1. 相关资料
可查阅 Qt 帮助
The Event System
和QEvent
部分
- 详细的 Qt 事件系统讲解, 带源码分析: http://blog.csdn.net/zerokkqq/article/details/6685389
- Qt 豆子相关文档: https://www.devbean.net/2012/10/qt-study-road-2-event-summary/
2. 附件说明
3. 相关类
QAbstractEventDispatcher
事件分发器.QEvent
QCoreApplication
,QApplication
QEventLoop
4. 概述
- 每个事件均为
QEvent
子类的一个实例, 每种事件均有一个枚举值用以识别事件类型. 可以使用QEvent::type()
来返回事件类型的枚举值, 从而用于比较.- 可使用
QEvent::registerEventType()
返回一个可用的枚举值.
- 可使用
5. 事件分类
根据事件发生与分发的不同, 可将事件分为三类:
- Spontaneous 事件, 从系统中得到消息(比如系统的按键, 鼠标等) , 转换为 QEvent 后, 被 Qt 事件系统处理.
- Posted 事件, 由 Qt 或应用程序产生, 被放入事件队列中 , 再通过事件循环处理. 使用 QCoreApplication::postEvnet() 发送事件.
- Send 事件, 由 Qt 或应用程序产生, 使用 QCoreApplication::sendEvent() 发送的事件.
示例: 窗口的重绘事件处理函数
PaintEvent()
, 可被以上这三种类型事件调用:- 窗口被覆盖或再次重新显示时, 系统产生自发事件 spontaneous 来请求重绘窗口.
- 手动调用
update()
函数, 产生 Posted 事件, 并放入消息队列. 由事件循环来请求重绘窗口. 该过程会被 Qt 优化(详见 Qt paintEvent()). - 手动调用
repaint()
函数, 产生 Send 事件, 直接调用该处理函数. 该过程不会被 Qt 优化.
5.1. Spontaneous事件发送
- 系统底层事件通过
QAbstractEventDispatcher
转换到 Qt 的事件循环中.
5.2. Post事件发送
- 需要在堆上开辟内存, 用完后会被自动删除.
- 使用 postEvent() 将事件放入事件队列, 并立即返回.
- 事件循环 QEventLoop 在空闲时判断事件队列是否为空, 最后使用 sendEvent() 派发这些事件队列中的事件.
- 可以手动使用: QCoreApplication::sendPostedEvents() 来派发事件队列中的所有事件(相当于清空了当前事件队列).
- 每个线程都有一个事件队列.
函数原型
1
2
3[static] void QCoreApplication::postEvent(QObject *receiver,
QEvent *event,
int priority = Qt::NormalEventPriority);使用示例:
1
2QApplication::postEvent(mainWin,
new QMouseEvent(QEvent::MouseButtonPress, pos, 0, 0, 0));
5.3. Send事件发送
- 需要在栈上开辟内存.
- 使用 sendEvent() 发送. 该函数会调用 notify() 进行事件分发, 并返回事件执行的返回值.
- 本过程不涉及 事件队列, 事件循环 等. 但仍可以进行事件过滤, 可使用全部的事件处理手段.
函数原型
1
2
3[static] bool QCoreApplication::sendEvent(
QObject *receiver,
QEvent *event);使用示例:
1
2QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
QApplication::sendEvent(mainWindow, &event);
6. 事件发送
- 事件分发器
QAbstractEventDispatcher
管理着 Qt 的事件队列, 从系统或其他事件源接收事件, 再发送给 QCoreApplication 或 QApplication 实例来进行处理.
6.1. 直接派发
- sendEvent 事件通过调用 QApplication::notify() 分发, 直接进入事件的派发和处理.
6.2. 通过事件循环方式
postEvent 和 自发事件
- Qt 主线程事件循环
- 使用 QCoreApplication::exec() 启动.
- 在 QCoreApplication::exit() 后退出.
- 本地事件循环使用 QEventLoop 构建.
6.2.1. 事件循环源码解析
步骤
- 处理 Qt 事件队列中的事件(也就是 post 事件), 直至为空.
- 处理系统消息队列中的消息(也就是自发事件), 直至为空.
- 执行第 2 步时, 会产生新的 Qt 事件, 然后继续处理 Qt 事件队列中的事件.
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14while (!exit_was_called) {
// 处理事件队列 中的事件
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
//(自发事件) 处理 系统消息队列 中的消息 (在处理过程中产生新的 Qt 事件)
while (!spontaneous_event_queue_is_empty) {
process_next_spontaneous_event();
}
// 对新产生的事件进行处理
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
}
7. 事件传递
- 一个事件产生后, 会先传送给最上层的子控件, 若在这个传送过程中没有被标记为被处理, 将会继续传送给其父控件组中其他可接受该事件的控件, 直到该事件被标记为已处理, 或到最顶层窗体.
- 事件的传播是在组件层次上面的, 而不是依靠类继承机制. eg: 若该组件不接受本事件, 则会继续传递给其父组件列表中能处理该事件的组件, 而不是父对象.
7.1. 过程
- 一个事件产生后, 会根据事件类型 不同, 而被以不同的方式分发出去.
- 不同方式分发出去的事件, 最终都要经过
QCoreApplication::notify()
来对事件进行处理. - 在 notify() 函数中, 先要将本事件传递给 receiver 处理.
- 如果在本 QCoreApplication 安装了事件过滤器, 本程序中发送给所有对象的事件都要经过这些事件过滤器处理. 若在其中都没有 return, 则继续传递.
- 如果在 receiver 安装了事件过滤器, 发送给该 receiver 的事件要先经过这些事件过滤器处理. 若在其中都没有 return, 则继续传递.
- 传递给 receiver 的 event() 函数进行处理. 若该事件为自定义事件, 会在函数中调用 customEvent() 函数. 而其他 Qt 事件, 一般会分发给其他 event handler 函数.
- 传递到特定 event handler 函数, 在其中对事件进行处理.
- event->accept(), 本事件传递结束.
- event->ignore(), 本事件继续向 receiver 的父控件传递.
- 传递本事件给 receiver 的父控件们, 直到传递过程被终止 (return), 事件被 accept, 或 到达顶层窗体.
7.2. 终止事件传递的方法
QApplication::notify()
有 bool 返回值- 只要返回, 就会终止事件传递过程. 且如果是 sendEvent(), 会返回值.
QObject::eventFilter()
,QObject::event()
有 bool 返回值.- return true : 表示事件已被处理, 不会继续传递
- return false : 表示事件需要继续传递
- 对于各事件处理函数 (eg: mouseReleaseEvent()), 无返回值.
- event->accept() : 表示事件已被处理, 不会继续传递
- event->ignore() : 表示事件需要继续传递
8. notify()
- 不论是哪种事件, 最后都由 notify() 负责派发.
- 在 notify 中, 负责调用各事件过滤器, event() 等. 可在本源码中明白事件传递的过程.
8.1. notify相关源码解析
- QApplication 重写了 QCoreApplication 的 notify(). 实现了众多 event 的分发.
- 每个 event 分发过程类似, 都会遍历 receiver 及其 父控件们. 直到中断整个事件传递过程.
1 | bool QCoreApplication::notify(QObject *receiver, QEvent *event) |
9. event()
- 源码如下:
1 | bool QObject::event(QEvent *e) |
— 道理越辩越明, 欢迎留言讨论. —